
import { ISize, IShaderCode } from "../interface/commTypes"
import GBufferTextures from "../interface/gBufferTextures"

function createGBuffer(device: GPUDevice, size: ISize,) {
    const position = device.createTexture({
        size,
        format: 'rgba16float',
        usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
    })
    const normal = device.createTexture({
        size,
        format: 'rgba16float',
        usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
    })
    const color =device.createTexture({
        size,
        format: 'rgba8unorm',
        usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
    })
    const depth = device.createTexture({
        size,
        format: 'depth24plus',
        usage: GPUTextureUsage.RENDER_ATTACHMENT,
    });

    return { position, normal, color, depth };
}

async function createGeometryPipeline(
    device: GPUDevice,
    shader: IShaderCode,
    size: ISize,
    NUM: number,
    gBufferTextures: GBufferTextures,
    cullMode: GPUCullMode = 'back',
) {

    const { vertex, fragment } = shader;

    const pipeline = device.createRenderPipeline({
        layout: 'auto',
        vertex: {
            module: device.createShaderModule({
                code: vertex,
            }),
            entryPoint: 'main',
            buffers: [{
                arrayStride: 8 * 4, // 3 position 2 uv,
                attributes: [
                    {
                        // position
                        shaderLocation: 0,
                        offset: 0,
                        format: 'float32x3',
                    },
                    {
                        // normal
                        shaderLocation: 1,
                        offset: 3 * 4,
                        format: 'float32x3',
                    },
                    {
                        // uv
                        shaderLocation: 2,
                        offset: 6 * 4,
                        format: 'float32x2',
                    },
                ]
            }
            ],
        },
        fragment: {
            module: device.createShaderModule({
                code: fragment,
            }),
            entryPoint: 'main',
            targets: [
                { format: 'rgba16float' }, // 位置
                { format: 'rgba16float' }, // 法线
                { format: 'rgba8unorm' },  // 颜色
            ],
        },
        primitive: {
            topology: 'triangle-list',
            cullMode: cullMode
        },
        depthStencil: {
            format: 'depth24plus',
            depthWriteEnabled: true,
            depthCompare: 'less',
        },
    } as GPURenderPipelineDescriptor);

    const modelViewAllBuffer = device.createBuffer({
        label: 'GPUBuffer store 4x4 matrix',
        size: 4 * 4 * 4, // 4 x 4 x float32
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
    })

    // create a 4x4xNUM STORAGE buffer to store matrix
    const modelViewBuffer = device.createBuffer({
        label: 'GPUBuffer store n*4x4 matrix',
        size: 4 * 4 * 4 * NUM, // 4 x 4 x float32 x NUM
        usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
    })
    // create a 4x4 uniform buffer to store projection
    const projectionBuffer = device.createBuffer({
        label: 'GPUBuffer store 4x4 matrix',
        size: 4 * 4 * 4, // 4 x 4 x float32
        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
    })
    // create a 4x4xNUM STORAGE buffer to store color
    const colorBuffer = device.createBuffer({
        label: 'GPUBuffer store n*4 color',
        size: 4 * 4 * NUM, // 4 x float32 x NUM
        usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
    })
    // create a uniform group for Matrix
    const vsGroup = device.createBindGroup({
        label: 'Uniform Group 4 geometry pipeline',
        layout: pipeline.getBindGroupLayout(0),
        entries: [
            {
                binding: 0,
                resource: {
                    buffer: modelViewBuffer
                }
            },
            {
                binding: 1,
                resource: {
                    buffer: projectionBuffer
                }
            },
            {
                binding: 2,
                resource: {
                    buffer: colorBuffer
                }
            },
            {
                binding: 3,
                resource: {
                    buffer: modelViewAllBuffer
                }
            },
        ]
    })

    let gbts = gBufferTextures;

    if(!gbts){
        gbts = createGBuffer(device, size);
    }
    
    const depthView = gbts.depth.createView()

    return { pipeline, vsGroup, modelViewAllBuffer, 
        modelViewBuffer, projectionBuffer, colorBuffer, 
        depthTexture: gbts.depth, depthView, gBufferTextures: gbts,
        shadowGroup: null,
     };
}

export { createGBuffer, createGeometryPipeline };